Bash Language Reference
Bash has an official language reference but frankly, it is far too long to be useful for quick readings. If you ever have a real question or actually want to understand what is going on, you should consult this behemoth, but otherwise I'm going to include some of my most frequented resources.
If you find any other language constructs to be particularly useful, please send a message to your TA.
Conditionals
Gosh these throw me off so much. To be completely safe when writing control flow in bash you should use the archaic if statement:
number=$1
if [ $a -gt 5 ]
then
echo "the number $a is greater than 5"
elif [ $a -lt 10 ]
then
echo "the number $a is less than 10"
else
echo "I only know numbers between 5 and 10... this is hard..."
Conditionals look a lot like flags. Some of the most common ones:
-eq
equals-ne
not equals-gt
strictly greater than-ge
greater than or equal to-lt
strictly less than-le
less than or equal to==
string comparison, equals!=
string comparison, not equals<
string comparison, less than (needs to be escaped with\
in[[ ]]
syntax)>
string comparison, greater than (needs to be escaped with\
in[[ ]]
syntax)-z
string is null (has zero length)-n
string is not null (has non-zero length)
There are more advanced test operators as well, as you will see.
Now, there is an advanced version of this that uses if [[ ... ]] then
syntax, and this is bash specific syntax, meaning that it does not translate to the POSIX shell directly. The key differences are as follows:
- You can use
&&
and||
like you would in C within the[[ ... ]]
environment. - You don't necessarily need to quote variables for comparison.
if [[ $str1 == $str2 ]]; then
echo "str1 is equal to str2"
fi
# is the same as
if [ "$str1" = "$str2" ]; then
echo "str1 is equal to str2"
fi - You do not need to fiercely escape everything. So using
\<
becomes<
in the[[ ... ]]
syntax. - You can match regex using the
=~
operator. This has the syntax[[ string =~ regex ]]
where if the regex can match the string, then the expression returns true.
Of these differences, the regex operator is the only real reason I see to use the [[ ... ]]
construct as opposed to the POSIX [ .. ]
, but at the end of the day, they are your scripts and you can do as you please.
Cases
Bash has support for the case
construct, similar to a switch statement in other languages. The construct takes the following form:
case $day in
"Monday")
echo "Start of the work week."
;;
"Wednesday")
echo "Midweek day."
;;
"Friday")
echo "End of the work week."
;;
*)
echo "Just another day."
;;
esac
Where $day
is a string. This is POSIX compliant, in case you were wondering (you probably weren't).
You can nest other control flow inside the case statement, consider the following example:
case $command in
"list")
echo "Listing items:"
items=("apple" "banana" "cherry")
for item in "${items[@]}"; do
echo "Item: $item"
done
;;
"greet")
echo "Hello, user!"
;;
"date")
echo "Current date and time: $(date)"
;;
*)
echo "Unknown command: $command"
echo "Available commands: list, greet, date"
;;
esac
Where $command
is, once again, a string.
For Loop
The for loop syntax is similar to what you would expect from any language, however bash does some strange python-esque-ish stuff that really makes my gizzard quiver. The simple version of a for loop is pretty much what you would expect:
for (( i = 1; i <= 10; i++ )); do
echo "$i"
done
The (( ... ))
construct is used for arithmetic evaluation and operations within control structures. This bleeds into another point that if you want to do any form of arithmetic in bash, you must do so within the (( ... ))
construct.
The (( ... ))
construct is not POSIX compliant
Bash has builtin support for a form of iterator if you need to iterate over an array of values. Consider the following example:
items=("apple" "banana" "cherry")
for item in "${items[@]}"; do
echo "Item: $item"
done
The syntax items[@]
ensures that every element of the array is treated as a separate element, which can prevent some unwanted splitting. Take for example the following array:
items=("apple pie" "banana split" "cherry tart")
If we use the naive reference to the array:
for item in $items; do
echo "Item: $item"
done
Then we will get the output:
Item: apple
Item: pie
Item: banana
Item: split
Item: cherry
Item: tart
Whereas when we use the correct syntax:
for item in "${items[@]}"; do
echo "Item: $item"
done
Then we get the output we expect:
Item: apple pie
Item: banana split
Item: cherry tart
While Loop
These are just a mixture of the conditional statements and a for loop really, put a conditional in and change the variable in the loop and you're golden:
while [ $number -gt 0 ]; do
echo "Countdown: $number"
number=$((number - 1))
done